use dotenv::dotenv;
use rmpredictdb::{
    model::ConvertAble,
    until::{build_pool, DB_POOL},
};
use serde_json::{Map, Value};
use tauri::{
    async_runtime::{block_on, spawn, JoinHandle},
    AppHandle, Manager,
};
use tauri_plugin_notification::NotificationExt;

use crate::{
    command::initialization_load,
    extensions_trait::Take,
    state_manage::{ChildProcessRatatuiState, UserState},
};
pub trait ValueExt {
    #[allow(dead_code)]
    fn trim(self) -> String;
    fn trim_ref(&self) -> String;
    fn convert<'a, T, E>(&'a self, _from: T, _to: E) -> E
    where
        T: From<&'a Map<String, Value>> + Take<OutPut = E>,
        E: ConvertAble;
}
impl ValueExt for Value {
    fn trim(self) -> String {
        self.as_str().unwrap().trim_matches('"').to_string()
    }

    /// 将Serde_Json Value 转换为Target
    /// # Arguments
    /// 泛型 E:要转换的目标值
    fn convert<'a, T, E>(&'a self, _from: T, _to: E) -> E
    where
        T: From<&'a Map<String, Value>> + Take<OutPut = E>,
        E: ConvertAble,
    {
        T::from(self.as_object().unwrap()).take()
    }

    fn trim_ref(&self) -> String {
        self.as_str().unwrap().trim_matches('"').to_string()
    }
}
trait WindowExt {
    fn show(&self, label: &'_ str);
    fn close(&self, label: &'_ str);
}
impl WindowExt for AppHandle {
    fn show(&self, label: &'_ str) {
        self.get_webview_window(label).unwrap().show().unwrap();
    }

    fn close(&self, label: &'_ str) {
        self.get_webview_window(label).unwrap().close().unwrap();
    }
}
fn notification(app: &AppHandle, title: &'_ str, message: &'_ str) {
    app.notification()
        .builder()
        .title(title.to_string())
        .body(message.to_string())
        .show()
        .unwrap();
}
pub fn connect_database(app: &AppHandle) -> JoinHandle<Result<(), ()>> {
    //连接后续换为云数据库
    dotenv().ok();
    let database_url = std::env::var("DATABASE_URL").unwrap_or_else(|_| {
        tracing::error!("DATABASE_URL must be set");
        std::process::exit(1);
    });

    let pool = build_pool(&database_url, 2);
    if let Err(_e) = pool {
        notification(app, "Database", "正在尝试重新连接数据库");
        let app_handle = app.clone();
        spawn(async move {
            if let Ok(pool_conn) = build_pool(&database_url, 2) {
                DB_POOL
                    .set(pool_conn)
                    .expect("Error connecting to database");
                Ok(())
            } else {
                notification(&app_handle, "Databse", "无法连接至数据库，正在关闭应用");
                Err(())
            }
        })
        //建立异步任务连接数据库
    } else {
        spawn(async move {
            DB_POOL
                .set(pool.unwrap())
                .expect("Error connecting to database");
            Ok(())
        })
    }
}
async fn init_work(app: &AppHandle) {
    let handle = connect_database(app);
    //在此处进行初始化工作
    match handle.await.unwrap() {
        Ok(_) => {
            match initialization_load(app) {
                Ok(user) => {
                    let state = app.state::<UserState>().inner();
                    let _ = state.update_instance(user);
                    app.close("splashscreen");
                    app.show("main");
                }
                Err(_) => {
                    app.close("splashscreen");
                    app.show("index");
                }
            };
        }
        Err(_) => {
            println!("正在离开应用");
            app.cleanup_before_exit();
            app.exit(0);
        }
    };
}
//计算最近登录日期与当前时间的差值
pub fn setup(app: &AppHandle) {
    //初始化工作
    std::thread::sleep(std::time::Duration::from_secs(5));
    block_on(async move {
        init_work(app).await;
    });
}

pub fn on_window_event(window: &tauri::Window, event: &tauri::WindowEvent) {
    //TODO: 保存state中的用户信息
    match event {
        tauri::WindowEvent::CloseRequested { .. } => {
            let handle = window.app_handle();
            println!("true");
            let state = handle.state::<ChildProcessRatatuiState>().inner();
            block_on(async move {
                state.kill().await;
            })
        }
        _ => {}
    }
}
